using System;
using System.Collections.Generic;

namespace MvvmFx.Common.ViewModels.Behaviors.Validation
{
    ///<summary>
    /// ValidationRules class is designed to provide support 
    /// to validate rules applied against properties of the 
    /// ViewModel object. 
    ///</summary>
    public class ValidationRules 
    {
        // list of broken rules for this business object.
        private BrokenRulesCollection _brokenRules;
        // threshold for short-circuiting to kick in
        private int _processThroughPriority;
        // reference to current ViewModelBase object
        private ViewModelBase _target;
        // reference to per-instance rules manager for this object
        private ValidationRulesManager _instanceRules;
        // reference to per-type rules manager for this object
        private ValidationRulesManager _typeRules;
        // reference to the active set of rules for this object
        private ValidationRulesManager _rulesToCheck;

        //used to synchronize various async operations
        private readonly object SyncRoot = new object();

        internal ValidationRules(ViewModelBase businessObject)
        {
            SetTarget(businessObject);
        }

        internal void SetTarget(ViewModelBase businessObject)
        {
            _target = businessObject;
        }

        internal ViewModelBase Target
        {
            get { return _target; }
        }

        private BrokenRulesCollection BrokenRulesList
        {
            get
            {
                if (_brokenRules == null)
                    _brokenRules = new BrokenRulesCollection();
                return _brokenRules;
            }
        }

        private ValidationRulesManager GetInstanceRules(bool createObject)
        {
            if (_instanceRules == null)
                if (createObject)
                    _instanceRules = new ValidationRulesManager();
            return _instanceRules;
        }

        private ValidationRulesManager GetTypeRules(bool createObject)
        {
            if (_typeRules == null)
                _typeRules = SharedValidationRules.GetManager(_target.GetType(), createObject);
            return _typeRules;
        }

        private ValidationRulesManager RulesToCheck
        {
            get
            {
                if (_rulesToCheck == null)
                {
                    ValidationRulesManager instanceRules = GetInstanceRules(false);
                    ValidationRulesManager typeRules = GetTypeRules(false);
                    if (instanceRules == null)
                    {
                        if (typeRules == null)
                            _rulesToCheck = null;
                        else
                            _rulesToCheck = typeRules;
                    }
                    else if (typeRules == null)
                        _rulesToCheck = instanceRules;
                    else
                    {
                        // both have values - consolidate into instance rules
                        _rulesToCheck = instanceRules;
                        foreach (KeyValuePair<string, RulesList> de in typeRules.RulesDictionary)
                        {
                            RulesList rules = _rulesToCheck.GetRulesForProperty(de.Key, true);
                            List<IRuleMethod> instanceList = rules.GetList(false);
                            instanceList.AddRange(de.Value.GetList(false));
                            List<string> dependancy = de.Value.GetDependancyList(false);
                            if (dependancy != null)
                                rules.GetDependancyList(true).AddRange(dependancy);
                        }
                    }
                }
                return _rulesToCheck;
            }
        }

        /// <summary>
        /// Returns an array containing the text descriptions of all
        /// validation rules associated with this object.
        /// </summary>
        /// <returns>String array.</returns>
        /// <remarks></remarks>
        public string[] GetRuleDescriptions()
        {
            List<string> result = new List<string>();
            ValidationRulesManager rules = RulesToCheck;
            if (rules != null)
            {
                foreach (KeyValuePair<string, RulesList> de in rules.RulesDictionary)
                {
                    List<IRuleMethod> list = de.Value.GetList(false);
                    for (int i = 0; i < list.Count; i++)
                    {
                        IRuleMethod rule = list[i];
                        result.Add(rule.ToString());
                    }
                }
            }
            return result.ToArray();
        }

        #region [ Short-Circuiting ]

        /// <summary>
        /// Gets or sets the priority through which
        /// CheckRules should process before short-circuiting
        /// processing on broken rules.
        /// </summary>
        /// <value>Defaults to 0.</value>
        /// <remarks>
        /// All rules for each property are processed by CheckRules
        /// though this priority. Rules with lower priorities are
        /// only processed if no previous rule has been marked as
        /// broken.
        /// </remarks>
        public int ProcessThroughPriority
        {
            get { return _processThroughPriority; }
            set { _processThroughPriority = value; }
        }

        #endregion

        #region [ Adding Instance Rules ]

        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// <para>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </para><para>
        /// The propertyName may be used by the method that implements the rule
        /// in order to retrieve the value to be validated. If the rule
        /// implementation is inside the target object then it probably has
        /// direct access to all data. However, if the rule implementation
        /// is outside the target object then it will need to use reflection
        /// or CallByName to dynamically invoke this property to retrieve
        /// the value to be validated.
        /// </para>
        /// </remarks>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="propertyName">
        /// The property name on the target object where the rule implementation can retrieve
        /// the value to be validated.
        /// </param>
        public void AddInstanceRule(RuleHandler handler, string propertyName)
        {
            GetInstanceRules(true).AddRule(handler, new RuleArgs(propertyName), 0);
        }

        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// <para>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </para><para>
        /// The propertyName may be used by the method that implements the rule
        /// in order to retrieve the value to be validated. If the rule
        /// implementation is inside the target object then it probably has
        /// direct access to all data. However, if the rule implementation
        /// is outside the target object then it will need to use reflection
        /// or CallByName to dynamically invoke this property to retrieve
        /// the value to be validated.
        /// </para>
        /// </remarks>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="propertyName">
        /// The property name on the target object where the rule implementation can retrieve
        /// the value to be validated.
        /// </param>
        /// <param name="priority">
        /// The priority of the rule, where lower numbers are processed first.
        /// </param>
        public void AddInstanceRule(RuleHandler handler, string propertyName, int priority)
        {
            GetInstanceRules(true).AddRule(handler, new RuleArgs(propertyName), priority);
        }

        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// <para>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </para><para>
        /// The propertyName may be used by the method that implements the rule
        /// in order to retrieve the value to be validated. If the rule
        /// implementation is inside the target object then it probably has
        /// direct access to all data. However, if the rule implementation
        /// is outside the target object then it will need to use reflection
        /// or CallByName to dynamically invoke this property to retrieve
        /// the value to be validated.
        /// </para>
        /// </remarks>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="propertyName">
        /// The property name on the target object where the rule implementation can retrieve
        /// the value to be validated.
        /// </param>
        /// <typeparam name="T">Type of the business object to be validated.</typeparam>
        public void AddInstanceRule<T>(RuleHandler<T, RuleArgs> handler, string propertyName)
        {
            GetInstanceRules(true).AddRule<T, RuleArgs>(handler, new RuleArgs(propertyName), 0);
        }

        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// <para>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </para><para>
        /// The propertyName may be used by the method that implements the rule
        /// in order to retrieve the value to be validated. If the rule
        /// implementation is inside the target object then it probably has
        /// direct access to all data. However, if the rule implementation
        /// is outside the target object then it will need to use reflection
        /// or CallByName to dynamically invoke this property to retrieve
        /// the value to be validated.
        /// </para>
        /// </remarks>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="propertyName">
        /// The property name on the target object where the rule implementation can retrieve
        /// the value to be validated.
        /// </param>
        /// <param name="priority">
        /// The priority of the rule, where lower numbers are processed first.
        /// </param>
        /// <typeparam name="T">Type of the business object to be validated.</typeparam>
        public void AddInstanceRule<T>(RuleHandler<T, RuleArgs> handler, string propertyName, int priority)
        {
            GetInstanceRules(true).AddRule<T, RuleArgs>(handler, new RuleArgs(propertyName), priority);
        }

        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </remarks>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="args">
        /// A RuleArgs object specifying the property name and other arguments
        /// passed to the rule method
        /// </param>
        public void AddInstanceRule(RuleHandler handler, RuleArgs args)
        {
            GetInstanceRules(true).AddRule(handler, args, 0);
        }

        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </remarks>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="args">
        /// A RuleArgs object specifying the property name and other arguments
        /// passed to the rule method
        /// </param>
        /// <param name="priority">
        /// The priority of the rule, where lower numbers are processed first.
        /// </param>
        public void AddInstanceRule(RuleHandler handler, RuleArgs args, int priority)
        {
            GetInstanceRules(true).AddRule(handler, args, priority);
        }

        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </remarks>
        /// <typeparam name="T">Type of the target object.</typeparam>
        /// <typeparam name="R">Type of the arguments parameter.</typeparam>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="args">
        /// A RuleArgs object specifying the property name and other arguments
        /// passed to the rule method
        /// </param>
        public void AddInstanceRule<T, R>(RuleHandler<T, R> handler, R args) where R : RuleArgs
        {
            GetInstanceRules(true).AddRule(handler, args, 0);
        }

        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </remarks>
        /// <typeparam name="T">Type of the target object.</typeparam>
        /// <typeparam name="R">Type of the arguments parameter.</typeparam>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="args">
        /// A RuleArgs object specifying the property name and other arguments
        /// passed to the rule method
        /// </param>
        /// <param name="priority">
        /// The priority of the rule, where lower numbers are processed first.
        /// </param>
        public void AddInstanceRule<T, R>(RuleHandler<T, R> handler, R args, int priority) where R : RuleArgs
        {
            GetInstanceRules(true).AddRule(handler, args, priority);
        }

        #endregion

        #region  [ Adding Shared Rules ]

        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// <para>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </para><para>
        /// The propertyName may be used by the method that implements the rule
        /// in order to retrieve the value to be validated. If the rule
        /// implementation is inside the target object then it probably has
        /// direct access to all data. However, if the rule implementation
        /// is outside the target object then it will need to use reflection
        /// or CallByName to dynamically invoke this property to retrieve
        /// the value to be validated.
        /// </para>
        /// </remarks>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="propertyName">
        /// The property name on the target object where the rule implementation can retrieve
        /// the value to be validated.
        /// </param>
        public void AddRule(RuleHandler handler, string propertyName)
        {
            ValidateHandler(handler);
            GetTypeRules(true).AddRule(handler, new RuleArgs(propertyName), 0);
        }

        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// <para>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </para><para>
        /// The propertyName may be used by the method that implements the rule
        /// in order to retrieve the value to be validated. If the rule
        /// implementation is inside the target object then it probably has
        /// direct access to all data. However, if the rule implementation
        /// is outside the target object then it will need to use reflection
        /// or CallByName to dynamically invoke this property to retrieve
        /// the value to be validated.
        /// </para>
        /// </remarks>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="propertyName">
        /// The property name on the target object where the rule implementation can retrieve
        /// the value to be validated.
        /// </param>
        /// <param name="priority">
        /// The priority of the rule, where lower numbers are processed first.
        /// </param>
        public void AddRule(RuleHandler handler, string propertyName, int priority)
        {
            ValidateHandler(handler);
            GetTypeRules(true).AddRule(handler, new RuleArgs(propertyName), priority);
        }


        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// <para>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </para><para>
        /// The propertyName may be used by the method that implements the rule
        /// in order to retrieve the value to be validated. If the rule
        /// implementation is inside the target object then it probably has
        /// direct access to all data. However, if the rule implementation
        /// is outside the target object then it will need to use reflection
        /// or CallByName to dynamically invoke this property to retrieve
        /// the value to be validated.
        /// </para>
        /// </remarks>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="propertyName">
        /// The property name on the target object where the rule implementation can retrieve
        /// the value to be validated.
        /// </param>
        public void AddRule<T>(RuleHandler<T, RuleArgs> handler, string propertyName)
        {
            ValidateHandler(handler);
            GetTypeRules(true).AddRule<T, RuleArgs>(handler, new RuleArgs(propertyName), 0);
        }

        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// <para>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </para><para>
        /// The propertyName may be used by the method that implements the rule
        /// in order to retrieve the value to be validated. If the rule
        /// implementation is inside the target object then it probably has
        /// direct access to all data. However, if the rule implementation
        /// is outside the target object then it will need to use reflection
        /// or CallByName to dynamically invoke this property to retrieve
        /// the value to be validated.
        /// </para>
        /// </remarks>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="args">
        /// A RuleArgs object specifying the property name and other arguments
        /// passed to the rule method
        /// </param>
        public void AddRule<T>(RuleHandler<T, RuleArgs> handler, RuleArgs args)
        {
            ValidateHandler(handler);
            GetTypeRules(true).AddRule<T, RuleArgs>(handler, args, 0);
        }

        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// <para>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </para><para>
        /// The propertyName may be used by the method that implements the rule
        /// in order to retrieve the value to be validated. If the rule
        /// implementation is inside the target object then it probably has
        /// direct access to all data. However, if the rule implementation
        /// is outside the target object then it will need to use reflection
        /// or CallByName to dynamically invoke this property to retrieve
        /// the value to be validated.
        /// </para>
        /// </remarks>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="propertyName">
        /// The property name on the target object where the rule implementation can retrieve
        /// the value to be validated.
        /// </param>
        /// <param name="priority">
        /// The priority of the rule, where lower numbers are processed first.
        /// </param>
        public void AddRule<T>(RuleHandler<T, RuleArgs> handler, string propertyName, int priority)
        {
            ValidateHandler(handler);
            GetTypeRules(true).AddRule<T, RuleArgs>(handler, new RuleArgs(propertyName), priority);
        }

        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </remarks>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="args">
        /// A RuleArgs object specifying the property name and other arguments
        /// passed to the rule method
        /// </param>
        public void AddRule(RuleHandler handler, RuleArgs args)
        {
            ValidateHandler(handler);
            GetTypeRules(true).AddRule(handler, args, 0);
        }

        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </remarks>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="args">
        /// A RuleArgs object specifying the property name and other arguments
        /// passed to the rule method
        /// </param>
        /// <param name="priority">
        /// The priority of the rule, where lower numbers are processed first.
        /// </param>
        public void AddRule(RuleHandler handler, RuleArgs args, int priority)
        {
            ValidateHandler(handler);
            GetTypeRules(true).AddRule(handler, args, priority);
        }

        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </remarks>
        /// <typeparam name="T">Type of the target object.</typeparam>
        /// <typeparam name="R">Type of the arguments parameter.</typeparam>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="args">
        /// A RuleArgs object specifying the property name and other arguments
        /// passed to the rule method
        /// </param>
        public void AddRule<T, R>(RuleHandler<T, R> handler, R args) where R : RuleArgs
        {
            ValidateHandler(handler);
            GetTypeRules(true).AddRule(handler, args, 0);
        }

        /// <summary>
        /// Adds a rule to the list of rules to be enforced.
        /// </summary>
        /// <remarks>
        /// A rule is implemented by a method which conforms to the 
        /// method signature defined by the RuleHandler delegate.
        /// </remarks>
        /// <typeparam name="T">Type of the target object.</typeparam>
        /// <typeparam name="R">Type of the arguments parameter.</typeparam>
        /// <param name="handler">The method that implements the rule.</param>
        /// <param name="args">
        /// A RuleArgs object specifying the property name and other arguments
        /// passed to the rule method
        /// </param>
        /// <param name="priority">
        /// The priority of the rule, where lower numbers are processed first.
        /// </param>
        public void AddRule<T, R>(RuleHandler<T, R> handler, R args, int priority) where R : RuleArgs
        {
            ValidateHandler(handler);
            GetTypeRules(true).AddRule(handler, args, priority);
        }

        private bool ValidateHandler(RuleHandler handler)
        {
            return ValidateHandler(handler.Method);
        }

        private bool ValidateHandler<T, R>(RuleHandler<T, R> handler) where R : RuleArgs
        {
            return ValidateHandler(handler.Method);
        }

        private bool ValidateHandler(System.Reflection.MethodInfo method)
        {
            if (!method.IsStatic && method.DeclaringType.IsInstanceOfType(_target))
                throw new InvalidOperationException(string.Format("{0}: {1}", Properties.Resources.InvalidRuleMethodException, method.Name));
            return true;
        }

        #endregion

        #region  [ Adding per-type dependencies ]

        /// <summary>
        /// Adds a property to the list of dependencies for
        /// the specified property
        /// </summary>
        /// <param name="propertyName">
        /// The name of the property.
        /// </param>
        /// <param name="dependentPropertyName">
        /// The name of the depandent property.
        /// </param>
        /// <remarks>
        /// When rules are checked for propertyName, they will
        /// also be checked for any dependent properties associated
        /// with that property.
        /// </remarks>
        public void AddDependentProperty(string propertyName, string dependentPropertyName)
        {
            GetTypeRules(true).AddDependentProperty(propertyName, dependentPropertyName);
        }

        /// <summary>
        /// Adds a property to the list of dependencies for
        /// the specified property
        /// </summary>
        /// <param name="propertyName">
        /// The name of the property.
        /// </param>
        /// <param name="dependantPropertyName">
        /// The name of the depandent property.
        /// </param>
        /// <remarks>
        /// When rules are checked for propertyName, they will
        /// also be checked for any dependent properties associated
        /// with that property.
        /// </remarks>
        [Obsolete("Use AddDependentProperty")]
        [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
        public void AddDependantProperty(string propertyName, string dependantPropertyName)
        {
            AddDependentProperty(propertyName, dependantPropertyName);
        }

        /// <summary>
        /// Adds a property to the list of dependencies for
        /// the specified property
        /// </summary>
        /// <param name="propertyName">
        /// The name of the property.
        /// </param>
        /// <param name="dependentPropertyName">
        /// The name of the depandent property.
        /// </param>
        /// <param name="isBidirectional">
        /// If <see langword="true"/> then a 
        /// reverse dependancy is also established
        /// from dependentPropertyName to propertyName.
        /// </param>
        /// <remarks>
        /// When rules are checked for propertyName, they will
        /// also be checked for any dependent properties associated
        /// with that property. If isBidirectional is 
        /// <see langword="true"/> then an additional association
        /// is set up so when rules are checked for
        /// dependentPropertyName the rules for propertyName
        /// will also be checked.
        /// </remarks>
        public void AddDependentProperty(string propertyName, string dependentPropertyName, bool isBidirectional)
        {
            ValidationRulesManager mgr = GetTypeRules(true);
            mgr.AddDependentProperty(propertyName, dependentPropertyName);
            if (isBidirectional)
                mgr.AddDependentProperty(dependentPropertyName, propertyName);
        }

        /// <summary>
        /// Adds a property to the list of dependencies for
        /// the specified property
        /// </summary>
        /// <param name="propertyName">
        /// The name of the property.
        /// </param>
        /// <param name="dependantPropertyName">
        /// The name of the depandent property.
        /// </param>
        /// <param name="isBidirectional">
        /// If <see langword="true"/> then a 
        /// reverse dependancy is also established
        /// from dependantPropertyName to propertyName.
        /// </param>
        /// <remarks>
        /// When rules are checked for propertyName, they will
        /// also be checked for any dependent properties associated
        /// with that property. If isBidirectional is 
        /// <see langword="true"/> then an additional association
        /// is set up so when rules are checked for
        /// dependantPropertyName the rules for propertyName
        /// will also be checked.
        /// </remarks>
        [Obsolete("Use AddDependentProperty")]
        [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
        public void AddDependantProperty(string propertyName, string dependantPropertyName, bool isBidirectional)
        {
            ValidationRulesManager mgr = GetTypeRules(true);
            mgr.AddDependentProperty(propertyName, dependantPropertyName);
            if (isBidirectional)
                mgr.AddDependentProperty(dependantPropertyName, propertyName);
        }

        #endregion

        #region [ Checking Rules ]

        private bool _suppressRuleChecking;

        /// <summary>
        /// Gets or sets a value indicating whether calling
        /// CheckRules should result in rule
        /// methods being invoked.
        /// </summary>
        /// <value>True to suppress all rule method invocation.</value>
        public bool SuppressRuleChecking
        {
            get { return _suppressRuleChecking; }
            set { _suppressRuleChecking = value; }
        }

        /// <summary>
        /// Invokes all rule methods associated with
        /// the specified property and any 
        /// dependent properties.
        /// </summary>
        /// <param name="propertyName">The name of the property to validate.</param>
        public string[] CheckRules(string propertyName)
        {
            if (_suppressRuleChecking)
                return new string[] {};

            var result = new List<string>();
            result.Add(propertyName);

            // get the rules dictionary
            ValidationRulesManager rules = RulesToCheck;
            if (rules != null)
            {
                // get the rules list for this property
                RulesList rulesList = rules.GetRulesForProperty(propertyName, false);
                if (rulesList != null)
                {
                    // get the actual list of rules (sorted by priority)
                    List<IRuleMethod> list = rulesList.GetList(true);
                    if (list != null)
                        CheckRules(list);
                    List<string> dependancies = rulesList.GetDependancyList(false);
                    if (dependancies != null)
                    {
                        for (int i = 0; i < dependancies.Count; i++)
                        {
                            string dependentProperty = dependancies[i];
                            result.Add(dependentProperty);
                            CheckRules(rules, dependentProperty);
                        }
                    }
                }
            }
            return result.ToArray();
        }

        private void CheckRules(ValidationRulesManager rules, string propertyName)
        {
            // get the rules list for this property
            RulesList rulesList = rules.GetRulesForProperty(propertyName, false);
            if (rulesList != null)
            {
                // get the actual list of rules (sorted by priority)
                List<IRuleMethod> list = rulesList.GetList(true);
                if (list != null)
                    CheckRules(list);
            }
        }

        /// <summary>
        /// Invokes all rule methods for all properties
        /// in the object.
        /// </summary>
        public void CheckRules()
        {
            if (_suppressRuleChecking)
                return;

            ValidationRulesManager rules = RulesToCheck;
            if (rules != null)
            {
                foreach (KeyValuePair<string, RulesList> de in rules.RulesDictionary)
                    CheckRules(de.Value.GetList(true));
            }
        }

        #endregion

        #region [ Check Rules ]

        /// <summary>
        /// Given a list
        /// containing IRuleMethod objects, this
        /// method executes all those rule methods.
        /// </summary>
        private void CheckRules(IList<IRuleMethod> list)
        {
            bool previousRuleBroken = false;
            bool shortCircuited = false;

            // Lock the rules here to ensure that all rules are run before allowing
            // async rules to notify that they have completed.

            for (int index = 0; index < list.Count; index++)
            {
                IRuleMethod rule = list[index];
                // see if short-circuiting should kick in
                if (!shortCircuited && (previousRuleBroken && rule.Priority > _processThroughPriority))
                    shortCircuited = true;

                if (shortCircuited)
                {
                    // we're short-circuited, so just remove
                    // all remaining broken rule entries
                    lock (SyncRoot)
                        BrokenRulesList.Remove(rule);
                }
                else
                {
                    // we're not short-circuited, so check rule
                    bool ruleResult;


                    try
                    {
                        ruleResult = rule.Invoke(_target);
                    }
                    catch (Exception ex)
                    {
                        //// force a broken rule
                        //ruleResult = false;
                        //rule.RuleArgs.Severity = RuleSeverity.Error;
                        //rule.RuleArgs.Description = 
                        //  string.Format(Properties.Resources.ValidationRuleException & "{{2}}", rule.RuleArgs.PropertyName, rule.RuleName, ex.Message);
                        // throw a more detailed exception
                        throw new ValidationException(
                            string.Format(Properties.Resources.ValidationRulesException, rule.RuleArgs.PropertyName, rule.RuleName), ex);
                    }

                    lock (SyncRoot)
                    {
                        if (ruleResult)
                        {
                            // the rule is not broken
                            BrokenRulesList.Remove(rule);
                        }
                        else
                        {
                            // the rule is broken
                            BrokenRulesList.Add(rule);
                            if (rule.RuleArgs.Severity == RuleSeverity.Error)
                            {
                                previousRuleBroken = true;
                            }
                        }
                    }

                    if (rule.RuleArgs.StopProcessing)
                    {
                        shortCircuited = true;
                        // reset the value for next time
                        rule.RuleArgs.StopProcessing = false;
                    }
                }
            }
        }

        #endregion

        #region [ Status Retrieval ]

        /// <summary>
        /// Returns a value indicating whether there are any broken rules
        /// at this time. 
        /// </summary>
        /// <returns>A value indicating whether any rules are broken.</returns>
        internal bool IsValid
        {
            get 
            {
                lock (SyncRoot)
                    return BrokenRulesList.ErrorCount == 0; 
            }
        }

        /// <summary>
        /// Returns a reference to the readonly collection of broken
        /// business rules.
        /// </summary>
        /// <remarks>
        /// The reference returned points to the actual collection object.
        /// This means that as rules are marked broken or unbroken over time,
        /// the underlying data will change. Because of this, the UI developer
        /// can bind a display directly to this collection to get a dynamic
        /// display of the broken rules at all times.
        /// </remarks>
        /// <returns>A reference to the collection of broken rules.</returns>
        public BrokenRulesCollection GetBrokenRules()
        {
            return BrokenRulesList;
        }

        #endregion
    }
}